package com.liugh.config.jackson;

import com.monitorjbl.json.JsonResult;
import com.monitorjbl.json.JsonView;
import com.monitorjbl.json.Match;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 对于一些需要以Json形式返回的类，在需要动态指定包含或者排除属性的情况下，需要用这个类进行处理
 */
public class JsonResultProcessor {


    /**
     * 简单的去掉对象中的handler
     * @param result
     * @param <T>
     * @return
     */
    public static <T> T processJsonResult(T result) {
        return processJsonResult(result, Rules.start().done());
    }


    /**
     * 对要转换成json的对象进行处理，可以指定需要排除和包含的属性
     * @param result
     * @param rules
     * @param <T>
     * @return
     */
    public static <T> T processJsonResult(T result, Rules rules) {
        if (null != result) {
            JsonView<T> jsonView = JsonView.with(result);
            if (null != rules && !rules.isEmpty()) {
                Set<Map.Entry<Class, InExRules>> rulesEntrySet = rules.getRules().entrySet();
                for (Map.Entry<Class, InExRules> entry : rulesEntrySet) {
                    jsonView = jsonView.onClass(entry.getKey(),
                            generateJsonMatch(entry.getValue().getExcludes(), entry.getValue().getIncludes()));
                }
            } else {
                jsonView.onClass(result.getClass(), generateJsonMatch());
            }
            return JsonResult.instance().use(jsonView).returnValue();
        } else {
            return result;
        }
    }


    /**
     * 生成jackson补丁 json view 的match
     * @return
     */
    private static Match generateJsonMatch(String... excludes) {
        // 由于mybatis lazy load会向对象中注入一个handler，因此这里也要排除所有名称为handler的属性,
        // 因为这个原因，所有自定义的pojo中不要出现handler属性
        return generateJsonMatch(excludes, null);
    }

    /**
     * 生成jackson补丁 json view 的match
     * @return
     */
    private static Match generateJsonMatch(String[] excludes, String[] includes) {
        // 由于mybatis lazy load会向对象中注入一个handler，因此这里也要排除所有名称为handler的属性,
        // 因为这个原因，所有自定义的pojo中不要出现handler属性
        Match match = Match.match().exclude("handler");     // 一定要排除掉handler
        if (null != excludes && excludes.length > 0) {
            match = match.exclude(excludes);
        }
        if (null != includes && includes.length > 0) {
            match = match.include(includes);
        }
        return match;
    }


    /**
     * 针对不同类的包含和排除规则
     */
    public static class Rules {

        private HashMap<Class, InExRules> mRules = new HashMap<>();


        private Rules() {}

        public static Rules start() {
            return new Rules();
        }

        public Rules and() {
            return this;
        }

        public Rules done() {
            return this;
        }

        /**
         * 链式调用，指定针对指定class的规则
         * @param cls
         * @return
         */
        public InExRules onClass(Class cls) {
            InExRules rules = mRules.get(cls);
            if (null == rules) {
                rules = new InExRules(this);
                mRules.put(cls, rules);
            }
            return rules;
        }

        private HashMap<Class, InExRules> getRules() {
            return mRules;
        }

        private boolean isEmpty() {
            return mRules.isEmpty();
        }

    }

    /**
     * 具体包含和排除的规则
     */
    public static class InExRules {

        private Rules rules;

        private Set<String> excludes = new HashSet<>();

        private Set<String> includes = new HashSet<>();

        InExRules(Rules rules) {
            this.rules = rules;
        }


        /**
         * 添加需要排除的属性
         * @param ex
         * @return
         */
        public InExRules addExcludes(String... ex) {
            if (null != ex && ex.length > 0) {
                for (String exRule : ex) {
                    excludes.add(exRule);
                }
            }
            return this;
        }

        /**
         * 添加需要包含的属性
         * @param in
         * @return
         */
        public InExRules addIncludes(String... in) {
            if (null != in && in.length > 0) {
                for (String inRule : in) {
                    includes.add(inRule);
                }
            }
            return this;
        }

        /**
         * 查看 {@link Rules#onClass(Class)}
         * @param className
         * @return
         */
        public InExRules onClass(Class className) {
            return this.rules.onClass(className);
        }


        /**
         * {@link Rules#and()}
         * @return
         */
        public Rules and() {
            return this.rules;
        }

        /**
         * {@link Rules#done()}
         * @return
         */
        public Rules done() {
            return this.rules;
        }



        public String[] getExcludes() {
            return excludes.toArray(new String[excludes.size()]);
        }

        public String[] getIncludes() {
            return includes.toArray(new String[includes.size()]);
        }
    }



}
